home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
3_0
/
MACBINAR
/
MBGET.C
next >
Wrap
C/C++ Source or Header
|
1988-06-14
|
9KB
|
539 lines
/*
Date: Tue, 12 Apr 88 22:11:44 EDT
From: singer@harvard.harvard.edu (Jon Hueras)
Subject: mbget.c
The comments below notwithstanding, this program does not do
Macterminal 1.1 style transfers, but rather MacBinary style
transfers. It still uses XModem transport protocol, but
this version has been modified to recognize and use CRCs
instead of 8-bit checksums when appropriate.
This program is supplied as-is, which is to say that this
is what I have been using for the past two years, ever
since I stopped using MacTerminal and started using Red
Ryder. I have used it successfully with Red Ryder versions
9.2 through 10.2 inclusive, and possibly even earlier versions
than that. I have no knowledge of its performance with any
other terminal emulation software.
Jon Hueras
Symantec/THINK Technologies
singer@endor.harvard.edu
*/
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <sgtty.h>
#ifdef NO_RENAME
#define rename(old, new) link(old, new); unlink(old)
#endif
#define RECORDBYTES 132
#define DATABYTES 128
#define NAMEBYTES 63
#define RETRIES 10
#define SOHTIMO 10
#define LINTIMO 20
#define CHRTIMO 2
#define MAXRECNO 0xff
#define BYTEMASK 0xff
#define TMO -1
#define DUP '\000'
#define SOH '\001'
#define EOT '\004'
#define ACK '\006'
#define NAK '\025'
#define CAN '\030'
#define EEF '\032'
#define ESC '\033'
#define H_NLENOFF 1
#define H_NAMEOFF 2
/* 65 <-> 80 is the FInfo structure */
#define H_TYPEOFF 65
#define H_AUTHOFF 69
#define H_LOCKOFF 81
#define H_DLENOFF 83
#define H_RLENOFF 87
#define H_CTIMOFF 91
#define H_MTIMOFF 95
#define H_OLD_DLENOFF 81
#define H_OLD_RLENOFF 85
#define TEXT 0
#define DATA 1
#define RSRC 2
#define FULL 3
int mode, txtmode;
struct macheader {
char m_name[NAMEBYTES+1];
char m_type[4];
char m_author[4];
long m_datalen;
long m_rsrclen;
long m_createtime;
long m_modifytime;
} mh;
struct filenames {
char f_info[256];
char f_data[256];
char f_rsrc[256];
} files;
char tmpname[16];
int lastack, crc;
char buf[DATABYTES];
/*
* macget -- receive file from macintosh using xmodem protocol
* Dave Johnson, Brown University Computer Science
*
* (c) 1984 Brown University
* may be used but not sold without permission
*
* created ddj 5/22/84
* revised ddj 6/29/84 -- added [-rdu] options
* revised ddj 7/16/84 -- protocol changes for MacTerminal Beta Version 0.5X
* revised ddj 7/31/84 -- pre-4.2 signal bugs fixed in timedout()
* revised ddj 11/7/84 -- renamed send_sync() -> get_sync()
* revised jfh 6/24/86 -- modified to do MacBinary transfers, rather than
* MacTerminal's mac-to-mac style transfers, and
* added CRC handling to the XModem layer. Also
* enabled the <ESC>B auto-receive feature. Changed
* name to mbget ('mb' for MacBinary) to avoid
* confusion with the original macget.
*/
char usage[] = "usage: \"mbget [-rdu] [filename]\"\n";
main(ac, av)
char **av;
{
char *name;
mode = FULL;
name = "";
ac--; av++;
while (ac) {
if (av[0][0] == '-') {
switch (av[0][1]) {
case 'r':
mode = RSRC;
break;
case 'd':
mode = DATA;
break;
case 'u':
mode = TEXT;
break;
default:
fprintf(stderr, usage);
exit(1);
}
}
else {
name = av[0];
}
ac--; av++;
}
setup_tty();
if (get_sync()) {
lastack = 0;
txtmode = 0;
recv_hdr(name);
if (mode == TEXT) txtmode++;
recv_file(files.f_data, mh.m_datalen, 1);
txtmode = 0;
recv_file(files.f_rsrc, mh.m_rsrclen, 0);
}
reset_tty();
}
recv_hdr(name)
char *name;
{
long get4();
int n;
FILE *fp;
char *np;
strcpy(tmpname, "#machdrXXXXXX");
mktemp(tmpname);
recv_file(tmpname, (long)DATABYTES, 1);
fp = fopen(tmpname, "r");
if (fp == NULL) {
perror("temp file");
cleanup(-1);
}
fread(buf, 1, DATABYTES, fp);
fclose(fp);
if (name && *name) {
n = strlen(name);
if (n > NAMEBYTES) n = NAMEBYTES;
strncpy(mh.m_name, name, n);
mh.m_name[n] = '\0';
}
else {
n = buf[H_NLENOFF] & BYTEMASK;
if (n > NAMEBYTES) n = NAMEBYTES;
strncpy(mh.m_name, buf + H_NAMEOFF, n);
mh.m_name[n] = '\0';
}
for (np = mh.m_name; *np; np++)
if (*np == ' ') *np = '_';
if (mode == FULL) {
sprintf(files.f_info, "%s.info", mh.m_name);
rename(tmpname, files.f_info);
tmpname[0] = '\0';
sprintf(files.f_data, "%s.data", mh.m_name);
sprintf(files.f_rsrc, "%s.rsrc", mh.m_name);
}
else {
unlink(tmpname);
tmpname[0] = '\0';
switch (mode) {
case RSRC:
sprintf(files.f_data, "/dev/null");
sprintf(files.f_rsrc, "%s.rsrc", mh.m_name);
break;
case DATA:
sprintf(files.f_data, "%s.data", mh.m_name);
sprintf(files.f_rsrc, "/dev/null");
break;
case TEXT:
sprintf(files.f_data, "%s", mh.m_name);
sprintf(files.f_rsrc, "/dev/null");
break;
}
}
strncpy(mh.m_type, buf + H_TYPEOFF, 4);
strncpy(mh.m_author, buf + H_AUTHOFF, 4);
mh.m_datalen = get4(buf + H_DLENOFF);
mh.m_rsrclen = get4(buf + H_RLENOFF);
mh.m_createtime = get4(buf + H_CTIMOFF);
mh.m_modifytime = get4(buf + H_MTIMOFF);
}
recv_file(fname, bytes, more)
char *fname;
long bytes;
int more;
{
register int status, n;
FILE *outf;
int naks = 0;
outf = fopen(fname, "w");
if (outf == NULL) {
perror(fname);
cleanup(-1);
}
for (;;) {
if (!bytes) {
if (!more)
tputc(ACK);
fclose(outf);
return;
}
status = rec_read(buf, DATABYTES);
switch (status) {
case EOT:
tputc(ACK);
fclose(outf);
if (more) {
purge(SOHTIMO);
cleanup(-1);
}
return;
case ACK:
tputc(ACK);
naks = 0;
n = (bytes > DATABYTES) ? DATABYTES : bytes;
bytes -= n;
fwrite(buf, n, 1, outf);
break;
case DUP:
tputc(ACK);
naks = 0;
break;
case NAK:
purge(CHRTIMO);
if (naks++ < RETRIES) {
tputc(NAK);
break;
}
/* fall through */
case CAN:
tputc(CAN);
fclose(outf);
/* unlink fname? */
cleanup(-1);
/* NOTREACHED */
}
}
}
get_sync()
{
int c, i;
for (;;) {
if ((c = tgetc(15)) == TMO)
break;
if (c != ESC)
continue;
if ((c = tgetc(1)) == TMO)
continue;
if (c == 'b')
break;
}
for (i = 0; i < 3; i++) {
tputc('C');
if ((c = tgetc(SOHTIMO)) != SOH)
continue;
tungetc(c);
crc++;
return 1;
}
for (i = 0; i < 3; i++) {
tputc(NAK);
if ((c = tgetc(SOHTIMO)) != SOH)
continue;
tungetc(c);
return 1;
}
tputc(CAN);
return 0;
}
rec_read(buf, recsize)
char buf[];
int recsize;
{
int c, rec, rec_bar, cksum;
long tgetrec();
c = tgetc(SOHTIMO);
switch (c) {
case TMO:
default:
return NAK;
case EOT:
return EOT;
case CAN:
return CAN;
case SOH:
/* read header */
rec = tgetc(CHRTIMO);
if (rec == TMO)
return NAK;
rec_bar = tgetc(CHRTIMO);
if (rec_bar == TMO)
return NAK;
/* check header */
if (rec != MAXRECNO - rec_bar) return NAK;
/* fill buffer */
if ((cksum = tgetrec(buf, recsize, LINTIMO)) == TMO)
return NAK;
/* get checksum */
c = tgetc(CHRTIMO);
if (c == TMO)
return NAK;
if (crc) {
if (c != ((cksum >> 8) & BYTEMASK))
return NAK;
c = tgetc(CHRTIMO);
if (c == TMO)
return NAK;
}
if (c != (cksum & BYTEMASK))
return NAK;
/* check record number */
if (rec == lastack)
return DUP;
if (rec != ((lastack + 1) & MAXRECNO))
return CAN;
else {
lastack = rec;
return ACK;
}
}
/* NOTREACHED */
}
purge(timeout)
int timeout;
{
int c;
do {
c = tgetc(timeout);
} while (c != TMO);
}
static int ttyfd;
static FILE *ttyf;
jmp_buf timobuf;
long tgetrec(buf, count, timeout)
char *buf;
int count, timeout;
{
char *bp;
int i, cksum;
if (setjmp(timobuf))
return TMO;
alarm(timeout);
i = fread(buf, 1, count, ttyf);
alarm(0);
if (i != count)
return TMO;
if (crc)
cksum = calcrc(buf, count);
if (!crc)
cksum = 0;
bp = buf;
for (i = 0; i < count; bp++, i++) {
if (!crc)
cksum += *bp;
if (txtmode && *bp == '\r')
*bp = '\n';
}
return cksum & 0xFFFF;
}
tgetc(timeout)
int timeout;
{
int c;
if (setjmp(timobuf))
return TMO;
alarm(timeout);
c = getc(ttyf);
alarm(0);
if (c == -1) /* probably hung up or logged off */
return EOT;
else
return c & BYTEMASK;
}
tungetc(c)
char c;
{
ungetc(c, ttyf);
}
tputc(c)
char c;
{
write(ttyfd, &c, 1);
}
timedout()
{
signal(SIGALRM, timedout); /* for pre-4.2 systems */
longjmp(timobuf, 1);
}
static struct sgttyb otty, ntty;
/* should turn messages off */
setup_tty()
{
int cleanup();
int timedout();
ttyf = stdin;
ttyfd = fileno(stdout);
ioctl(ttyfd, TIOCGETP, &otty);
signal(SIGHUP, cleanup);
signal(SIGINT, cleanup);
signal(SIGQUIT, cleanup);
signal(SIGTERM, cleanup);
signal(SIGALRM, timedout);
ntty = otty;
ntty.sg_flags = RAW|ANYP;
ioctl(ttyfd, TIOCSETP, &ntty);
}
reset_tty()
{
sleep(5); /* should wait for output to drain */
ioctl(ttyfd, TIOCSETP, &otty);
}
cleanup(sig)
int sig;
{
if (tmpname[0] != '\0')
unlink(tmpname);
reset_tty();
exit(sig);
}
long
get4(bp)
char *bp;
{
register int i;
long value = 0;
for (i = 0; i < 4; i++) {
value <<= 8;
value |= (*bp & BYTEMASK);
bp++;
}
return value;
}
int calcrc(ptr, count)
char *ptr;
int count;
{
int crc, i;
crc = 0;
while (--count >= 0) {
crc ^= ((int) *ptr++) << 8;
for (i = 0; i < 8; ++i)
if (crc & 0x8000)
crc = crc << 1 ^ 0x1021;
else
crc <<= 1;
}
return (crc & 0xFFFF);
}